package de.upb.messerschmidt.set;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;

import com.google.gson.Gson;

import de.upb.messerschmidt.set.entity.Person;
import de.upb.messerschmidt.set.jdo.PMF;
import de.upb.messerschmidt.set.oauth.Authored;
import de.upb.messerschmidt.set.oauth.Document;
import de.upb.messerschmidt.set.oauth.MendeleyUrls;
import de.upb.messerschmidt.set.oauth.OAuthData;
import de.upb.messerschmidt.set.util.Utils;

/**
 * This Servlet returns all {@link Document}s of a specific user. This needs the
 * user's OAuth data because of a bug in the Mendeley API.
 * 
 * @author Tim Messerschmidt
 * 
 */
public class GetPublicationServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private static OAuthConsumer consumer;

	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String user = req.getParameter("user");

		if (user != null) {
			getPublications(user, resp);
		} else {
			resp.getWriter().println("Please specify the User");
		}
	}

	@SuppressWarnings("unchecked")
	private void getPublications(String user, HttpServletResponse resp)
			throws IOException {
		Person userPerson = null;

		String[] userName = Utils.nameParser(user);

		PersistenceManager pm = PMF.get().getPersistenceManager();

		Query personQuery = pm.newQuery(Person.class);
		personQuery.setFilter("name == nameParam && prename == prenameParam");
		personQuery.declareParameters("String nameParam, String prenameParam");

		List<Person> personList = (List<Person>) personQuery.execute(
				userName[0], userName[1]);

		if (!personList.isEmpty()) {
			userPerson = personList.get(0);
		}

		if (userPerson != null) {
			String authored = getAuthored(userPerson.getToken(),
					userPerson.getSecret(), resp);

			List<String> documents = parseAuthored(authored, resp);

			if (documents != null)
				getDocuments(documents, resp);
		} else {
			resp.getWriter().println("User not found");
		}

		pm.close();
	}

	/**
	 * Starts API-requests for documents from the supplied list and returns more
	 * data for them via the response
	 * 
	 * @param documents
	 *            the document id's we want to know more about
	 * @param resp
	 *            the response needed to reply
	 * @throws IOException
	 */
	private void getDocuments(List<String> documents, HttpServletResponse resp)
			throws IOException {
		ArrayList<Document> documentList = new ArrayList<Document>();
		Gson gson = new Gson();
		for (String s : documents) {
			URL url;
			try {
				url = new URL(String.format(
						MendeleyUrls.MENDELEY_DOCUMENT_WITH_ID, s));
				HttpURLConnection request = (HttpURLConnection) url
						.openConnection();
				consumer.sign(request);
				request.connect();
				BufferedReader in = new BufferedReader(new InputStreamReader(
						request.getInputStream()));
				String inputLine;
				String answer = "";
				while ((inputLine = in.readLine()) != null)
					answer += URLDecoder.decode(inputLine, "UTF-8");
				in.close();

				Document tmp = null;
				tmp = gson.fromJson(answer, Document.class);

				if (tmp != null) {
					documentList.add(tmp);
				}
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (OAuthMessageSignerException e) {
				e.printStackTrace();
			} catch (OAuthExpectationFailedException e) {
				e.printStackTrace();
			} catch (OAuthCommunicationException e) {
				e.printStackTrace();
			}
		}
		if (!documentList.isEmpty()) {
			resp.getWriter().println(
					URLEncoder.encode(gson.toJson(documentList), "UTF-8"));
		} else {
			resp.getWriter().println(
					URLEncoder.encode("no documents found", "UTF-8"));
		}
	}

	/**
	 * This parsed the authored reply and returns a list with ids of all the
	 * documents the user (co-)authored
	 * 
	 * @param authored
	 *            Mendeley's reply
	 * @param resp
	 *            the response needed to reply
	 * @return a list of ids
	 * @throws IOException
	 */
	private List<String> parseAuthored(String authored, HttpServletResponse resp)
			throws IOException {
		List<String> ids = new ArrayList<String>();

		Gson gson = new Gson();
		// Parse the JSON reply
		Authored tmp = gson.fromJson(authored, Authored.class);
		if (tmp.getDocument_ids() != null) {
			for (String i : tmp.getDocument_ids()) {
				ids.add(i);
			}
			return ids;
		}
		return null;
	}

	/**
	 * This method gets a server reply from Mendeley's API. It contains a list
	 * of documents the user (co-)authored
	 * 
	 * @param token
	 *            the user's OAuth token
	 * @param secret
	 *            the user's OAuth secret
	 * @param resp
	 *            the response we want to print on
	 * @return Mendeley's response
	 * @throws IOException
	 */
	private String getAuthored(String token, String secret,
			HttpServletResponse resp) throws IOException {
		try {
			URL url = new URL(MendeleyUrls.MENDELEY_AUTHORED);
			consumer = new DefaultOAuthConsumer(OAuthData.CONSUMERKEY,
					OAuthData.CONSUMERSECRET);
			consumer.setTokenWithSecret(token, secret);

			HttpURLConnection request = (HttpURLConnection) url
					.openConnection();

			consumer.sign(request);
			request.connect();

			BufferedReader in = new BufferedReader(new InputStreamReader(
					request.getInputStream()));
			String inputLine;
			String answer = "";

			while ((inputLine = in.readLine()) != null)
				answer += inputLine;
			in.close();

			return answer;
		} catch (Exception e) {
			resp.getWriter().println(e);
			return null;
		}
	}
}
